/* 
 =============================================================================
 * Program Name 	: parser.y
 * Author		: #B96902133, Hakki Caner Kirmizi
 * Date			: 20100616
 * Description		: Parser implementation for 'imaginative' csie 
 *			  programming language written with GNU Bison.
 * Environment		: Windows-7 Entreprise
 * Editor		: vim 7.2 for WIN 
 * Integration Tests	: linux2.csie.ntu.edu.tw
 * Flex			: 2.5.35
 * Bison		: bison (GNU Bison) 2.4.1
 * Compiler		: gcc (Debian 4.4.4-5) 4.4.4
 * Version Control	: TortoiseSVN 1.6.8, Build 19260 - 32 Bit
 * Project Hosting	: http://code.google.com/p/csie-compiler/
 * Notes		:
 * ----------------------
 * 1) This program is using an external hash table library which is 
 * implemented by Christopher Clark. The library is under new BSD licence.
 *    Please refer to --> http://www.cl.cam.ac.uk/~cwc22/hashtable/
 *
 =============================================================================
 */

	/* ----- DEFINITIONS PART ----- */
	/* ============================ */
%{
	#include <stdio.h>
	#include <stdlib.h>
	#include "gen_code.h"
	#include "lib/hashtable.h"
	#include "lib/hashtable_itr.h"

	/* Code below is for hash table declarations */
	/* ----------------------------------------- */

	typedef unsigned int uint32_t;
	typedef unsigned short uint16_t;
	
	struct key {
		uint32_t one_ip; uint32_t two_ip; uint16_t one_port; uint16_t two_port;
	};
	
	struct value {
		char *id;
		int type; // 0: varible, 1: array
		int local_index; // register no
		int value; // if this is 0-type, value is used for storage
		int dimension; // // if this is 1-type, this keeps the total dim of array
		struct array_index {
			int which_dim;
			int *index; // size of dimension
		} ai; // this is for each dimension of array
	};

	DEFINE_HASHTABLE_INSERT(insert_some, struct key, struct value);
	DEFINE_HASHTABLE_SEARCH(search_some, struct key, struct value);
	DEFINE_HASHTABLE_REMOVE(remove_some, struct key, struct value);
	DEFINE_HASHTABLE_ITERATOR_SEARCH(search_itr_some, struct key);
	
	static unsigned int hashfromkey(void *ky) {
		struct key *k = (struct key *)ky;
		return (((k->one_ip << 17) | (k->one_ip >> 15)) ^ k->two_ip) +
				(k->one_port * 17) + (k->two_port * 13 * 29);
	}

	static int equalkeys(void *k1, void *k2) {
		return (0 == memcmp(k1,k2,sizeof(struct key)));
	}
	
	struct key *k, *kk;
	struct value *v, *found;
	struct hashtable *h;
	struct hashtable_itr *itr;
	
	/* Code below is internal global variable declarations for parser */
	/* -------------------------------------------------------------- */	
	
	//struct array_index *ai;
	FILE *bass_file;
	extern int yyparse();
	char *var_id;
	int array_id[10];
	int array_i = 0;
	int var_num = 1;
	int arr_dim = 0;
	int multiarraydim = 0;
	int isinit = 1;
	int if_scope_out = -1;
	int false_label = 0;
	int cond_out_label = 64;
	int while_label = 128;
	int var_value = 0;
	int operator; // 0:> , 1:>= , 2:< , 3:<= , 4:== , 5:!=, 6:AND, 7:OR, 8:NOT
	int bool1;
	int bool2;
	int bool_true = 0;
	int bool_false = 64;

	/* Code below is functions used through semantic actions */
	/* ----------------------------------------------------- */
	
	int str_to_int(char *str) {
		int i = 0;
		while(*str != '\0') {
			i += (int)*str;
			str++;
		}
		return i;
	}
	
	void create_sym(char *id, int type, int var_num, int dim) {
		k = (struct key *)malloc(sizeof(struct key));
	        if (NULL == k) {
            		printf("ran out of memory allocating a key\n");
	            	exit(1);
        	}
		
		int st_index = str_to_int(id);
		
		printf("Hash key initializer of [%s] is [%d]\n", id, st_index);
		
	        k->one_ip = 0xcfccee40 + st_index;
       	 	k->two_ip = 0xcf0cee67 - (5 * st_index);
     		k->one_port = 22 + (7 * st_index);
	        k->two_port = 5522 - (3 * st_index);
		
		v = (struct value *)malloc(sizeof(struct value));
		v->id = id;
		v->type = type;
		v->local_index = var_num;
		v->dimension = dim;
		
        	if (!insert_some(h,k,v)) exit(-1); // out of mem
			printf("Symbol [%s] inserted with local_index: [%s->%d]\n", 
				v->id, v->id, v->local_index);
			printf("Symbol table now contains %u item(s).\n", hashtable_count(h));		
	}
	
	struct value* find_sym(char *id) {
		int st_index = str_to_int(id);
		k = (struct key *)malloc(sizeof(struct key));
		if (NULL == k) {
			printf("ran out of memory allocating a key\n");
			return NULL;
		}
		
		k->one_ip = 0xcfccee40 + st_index;
	        k->two_ip = 0xcf0cee67 - (5 * st_index);
       		k->one_port = 22 + (7 * st_index);
	        k->two_port = 5522 - (3 * st_index);
		
		if (NULL == (found = search_some(h,k))) {
       			printf("Key not found for [%s]\n", id);
			return NULL;
	        } else {
			//printf("Symbol found for [%s]\n", id);
			return found;
		}
	}
	
	void assign_variable(char *var) {
		found = find_sym(var);
		int arg = found->local_index;
		printf("Found the local index of varible | local_index is: [%s->%d]\n", var, arg);
		printf("Initializing variable [%s] to [%d]...\n", var, var_value);
		found->value = var_value;
		gen_code(bass_file, ISTORE, arg);
	}
	
	
	void assign_array(char *arr, int dim) {
		found = find_sym(arr);
		int arg = found->local_index;
		printf("Assigning symbol [%s]...\n", arr);
		if (dim==1) {
			int i = array_i--;
			int val = array_id[i];
			found->ai.index[val] = var_value;
			gen_code(bass_file, IASTORE, 0);
		}
		else if (dim>1) {
			int i = array_i--;
			int val = array_id[i];
			found->ai.index[val] = var_value;
			gen_code(bass_file, IASTORE, 0);
		}
	}
	
	void new_dimension(struct value *v, int num_of_index, int dim) {
		v->ai.index = malloc(sizeof(int) * num_of_index);
		v->ai.which_dim = dim;
	}
	
	void iterate_hashtable() {
		printf("Below is hash entries with their contents:\n");
		itr = hashtable_iterator(h);
		int i = 0;
		if (hashtable_count(h) > 0) {
			do {
				kk = hashtable_iterator_key(itr);
				v = hashtable_iterator_value(itr);
				printf("id: %s | \t\ttype:%d | \tlocal_index: %d | \tval: %d | \tai.which_dim: %d | \tai.(*index): \n", 
						v->id, v->type, v->local_index, v->value, v->ai.which_dim);
				i++;
			} while (hashtable_iterator_advance(itr));
		}
	}
	
%}

/* Data types used for tokens */
/* -------------------------- */
%union {
	char *id;
	int num;
}

/* Tokens coming from scanner.l */
/* ---------------------------- */

%start program
%token <id> ID 
%token <num> NUMBER 
%token INTSYM IFSYM ELSESYM WHILESYM BREAKSYM CONTINUESYM SCANSYM PRINTSYM
%right '='
%left OR
%left AND
%left EQUAL NOTEQ
%left GREATERTHAN GREATEREQ LESSTHAN LESSEQ
%left '+' '-'
%left '*' '/'
%right POS NEG NOT

%type <id> var


%%	/* ----- GRAMMAR RULES PART ----- */
	/* ============================== */

program		: {
		  // initialize the symbol table in the beginning of
		  // program. This place ensures global scope;) 
		  gen_global_main(bass_file); 
		  h = create_hashtable(16, hashfromkey, equalkeys);
		  if (NULL == h) exit(-1); // out of memory
		  }
		blockStmt 
		{
		  iterate_hashtable();
		  gen_code(bass_file, RETURN, 0);
		  // free symbol table
		  hashtable_destroy(h, 1);
    		  free(k);
		}
		;
			
blockStmt	: '{' varDeclInit stmtList '}'
		;

varDeclInit	: /* lambda */
	 	| INTSYM intArrayInit ID 
		{ 
		  if (arr_dim==0) {
		    printf("Creating a symbol for [%s] varible in the table...\n", $3);
		    create_sym($3, 0, var_num, 0);
		    var_num++;
		  }
		  else if (arr_dim==1) {
		    printf("Creating a symbol for [%d-dim] array [%s] in the table...\n", arr_dim, $3);
			create_sym($3, 1, var_num, arr_dim);
			found = find_sym($3);
			array_i--;
			int temp = 0;
			while (temp <= array_i) {
			  gen_code(bass_file, LDC, array_id[temp]);
			  new_dimension(found, array_id[temp], array_i);
			  temp++;
			}
			gen_code(bass_file, NEWARRAY, 0);
			gen_code(bass_file, ASTORE, var_num);
			var_num++;
			arr_dim=0;
		  } 
		  else if (arr_dim>1) {
		    printf("Creating a symbol for [%d-dim] array [%s] in the table...\n", arr_dim, $3);
		    create_sym($3, 1, var_num, arr_dim);
			found = find_sym($3);
			array_i--;
			int temp = 0;
			while (temp <= array_i) {
			  gen_code(bass_file, LDC, array_id[temp]);
			  new_dimension(found, array_id[temp], temp);
			  temp++;
			}
			gen_code(bass_file, MULTIANEWARRAY, arr_dim);
			gen_code(bass_file, ASTORE, var_num);
			var_num++;
			arr_dim=0;
		  }
		  array_i = 0; // clean array_id
		}
		idList ';' varDeclInit
	 	;

intArrayInit	: /* lambda */
	  	| '[' NUMBER 
		{
		  printf("Size of dimension (%d) is: %d\n", array_i, $2);
		  array_id[array_i] = $2;
		  array_i++;
		}
		']' 
		{
		  arr_dim++;
		  isinit = 1;
		}
		intArrayInit 
		;

idList		: /* lambda */
		| ',' ID 
		{ 
		  if (arr_dim==0) {
		    printf("Creating a symbol for [%s] variable in the table...\n", $2);
		    create_sym($2, 0, var_num, 0);
		    var_num++;
		  }
		  else if (arr_dim==1) {
		    printf("Creating a symbol for [%d-dim] array [%s] in the table...\n", arr_dim, $2);
			create_sym($2, 1, var_num, arr_dim);
			found = find_sym($2);
			array_i--;
			int temp = 0;
			while (temp <= array_i) {
			  gen_code(bass_file, LDC, array_id[temp]);
			  new_dimension(found, array_id[temp], array_i);
			  temp++;
			}
			gen_code(bass_file, NEWARRAY, 0);
			gen_code(bass_file, ASTORE, var_num);
			var_num++;
			arr_dim=0;
		  } 
		  else if (arr_dim>1) {
		    printf("Creating a symbol for [%d-dim] array [%s] in the table...\n", arr_dim, $2);
		    create_sym($2, 1, var_num, arr_dim);
			found = find_sym($2);
			array_i--;
			int temp = 0;
			while (temp <= array_i) {
			  gen_code(bass_file, LDC, array_id[temp]);
			  gen_code(bass_file, AALOAD, 0);
			  new_dimension(found, array_id[temp], temp);
			  temp++;
			}
			gen_code(bass_file, MULTIANEWARRAY, arr_dim);
			gen_code(bass_file, ASTORE, var_num);
			var_num++;
			arr_dim=0;
		  }
		  array_i = 0; // clean array_id
		}
		idList
		;
			
var		: ID 
		{
		  found = find_sym($1);
		  if (found->type == 0) { // var is variable
		    int arg = found->local_index;
			printf("Found variable [%s] var in [var] rule. local index is: [%s->%d]\n", 
					found->id, found->id, arg);
		    var_value = found->value;
		    var_id = found->id;
		  }
		  else if (found->type == 1) { // var is array
		    int arg = found->local_index;
			printf("Found variable [%s] array in [var] rule. local index is: [%s->%d] | dim is [%s->%d]\n", 
					found->id, found->id, arg, found->id, found->dimension);
			int i = array_i--;
		    gen_code(bass_file, ALOAD, arg);
			array_i++;
			if (found->dimension > 1)
			  multiarraydim = found->dimension;
			  isinit = 0;
		  }
		}
		arrayInit
		;

varList		: /* lambda */
	 	| ',' var 
	 	{ 
		  found = find_sym($2);
		  int arg = found->local_index;
		  printf("Found variable [%s] in [varList] rule. local index is: [%s->%d]\n", found->id, found->id, arg);
		  gen_code(bass_file, INVOKESTATIC, 0);
		  gen_code(bass_file, READINT, 0);
		  gen_code(bass_file, ISTORE, arg);
		}
		varList
	 	;

stmtList	: /* lambda */
      		| stmt stmtList
      		;

arrayInit	: /* lambda */
	      	| '[' arithExpr 
		{
		  array_id[array_i] = var_value;
		  array_i++;
		}
		']' arrayInit
		;

arithExprList	: /* lambda */
	      	| ',' arithExpr 
		{
		  gen_code(bass_file, INVOKESTATIC, 0);
		  gen_code(bass_file, PRINTLNINT, 0);
		}
		arithExprList
	      	;

stmt		: var '=' arithExpr ';'
		{
		  found = find_sym($1);
		  if (found->type == 0) { // var is variable (not array)
		    printf("Initializing [%s] variable in [stmt] rule...\n", $1, found->dimension);
		    assign_variable($1);
		  }
		  else if (found->type == 1) { // var is array
		    printf("Initializing [%s] which is [%d-dim array] in [stmt] rule...\n", $1, found->dimension);
			assign_array($1, found->dimension);
		  }
		  multiarraydim = 0;
		}
		| IFSYM '(' 
		{
		  // false_label here is for else block
		  false_label++; // B.false
		}
		boolExpr ')' 
		{
		  if(operator==0) { 	   // operator: >
		    gen_code(bass_file, IFICMPLE, false_label);
		  } else if(operator==1) { // operator: >=
		    gen_code(bass_file, IFICMPLT, false_label);
		  } else if(operator==2) { // operator: <
		    gen_code(bass_file, IFICMPGE, false_label);
		  } else if(operator==3) { // operator: <=
		    gen_code(bass_file, IFICMPGT, false_label);
		  } else if(operator==4) { // operator: ==
		    gen_code(bass_file, IFICMPNE, false_label);
		  } else if(operator==5) { // operator: !=
		    gen_code(bass_file, IFICMPEQ, false_label);
		  } else if(operator==6) { // operator: AND
		    gen_code(bass_file, IAND, 0);
			gen_code(bass_file, IFEQ, false_label);
		  } else if(operator==7) { // operator: OR
		    gen_code(bass_file, IOR, 0);
			gen_code(bass_file, IFEQ, false_label);
		  } else if(operator==8) { // operator: NOT
		    gen_code(bass_file, LDC, 1);
		    gen_code(bass_file, IXOR, 0);
			gen_code(bass_file, IFEQ, false_label);
		  }
		}
		blockStmt elseBlock
		| WHILESYM '(' 
		{
		  gen_code(bass_file, LABEL_NUM, while_label);
		}
		boolExpr ')' 
		{
		  while_label++;
		  if(operator==0) { 	   // operator: >
		    gen_code(bass_file, IFICMPLE, while_label);
		  } else if(operator==1) { // operator: >=
		    gen_code(bass_file, IFICMPLT, while_label);
		  } else if(operator==2) { // operator: <
		    gen_code(bass_file, IFICMPGE, while_label);
		  } else if(operator==3) { // operator: <=
		    gen_code(bass_file, IFICMPGT, while_label);
		  } else if(operator==4) { // operator: ==
		    gen_code(bass_file, IFICMPNE, while_label);
		  } else if(operator==5) { // operator: !=
		    gen_code(bass_file, IFICMPEQ, while_label);
		  } else if(operator==6) { // operator: AND
		    gen_code(bass_file, IAND, 0);
			gen_code(bass_file, IFEQ, while_label);
		  } else if(operator==7) { // operator: OR
		    gen_code(bass_file, IOR, 0);
			gen_code(bass_file, IFEQ, while_label);
		  } else if(operator==8) { // operator: NOT
		    gen_code(bass_file, LDC, 1);
		    gen_code(bass_file, IXOR, 0);
			gen_code(bass_file, IFEQ, while_label);
		  }
		}
		blockStmt
		{
		  while_label--;
		  gen_code(bass_file, GOTO, while_label);
		  while_label++;
		  gen_code(bass_file, LABEL_NUM, while_label);
		  while_label++;
		}
		| BREAKSYM ';'
		| CONTINUESYM ';'
		| SCANSYM '(' var 
		{
		  found = find_sym($3);
		  int arg = found->local_index;
		  gen_code(bass_file, INVOKESTATIC, 0);
		  gen_code(bass_file, READINT, 0);
		  gen_code(bass_file, ISTORE, arg);
		}
		varList ')' ';'
		| PRINTSYM '(' arithExpr
		{
		  gen_code(bass_file, INVOKESTATIC, 0);
		  gen_code(bass_file, PRINTLNINT, 0);
		}
		arithExprList ')' ';'
		| blockStmt
		;
		
elseBlock 	: /* lambda */
		{
		  gen_code(bass_file, LABEL_NUM, false_label);
		}
		| 
		{
		  cond_out_label++;
		  gen_code(bass_file, GOTO, cond_out_label);
		}
		ELSESYM 
		{
		  gen_code(bass_file, LABEL_NUM, false_label);
		}
		blockStmt
		{
		  gen_code(bass_file, LABEL_NUM, cond_out_label);
		}
		;
		
arithExpr	: arithExpr '+' arithExpr 
		{
		  gen_code(bass_file, IADD, 0);
		}
		| arithExpr '-' arithExpr
		{
		  gen_code(bass_file, ISUB, 0);
		}
		| arithExpr '*' arithExpr
		{
		  gen_code(bass_file, IMUL, 0);
		}
		| arithExpr '/' arithExpr
		{
		  gen_code(bass_file, IDIV, 0);
		}
		| '+' arithExpr %prec POS
		| '-' arithExpr %prec NEG
		{
		  gen_code(bass_file, INEG, 0);
		}
		| NUMBER
		{
		  gen_code(bass_file, LDC, $1);
		  var_value = $1;
		  // Because we have to ARR-ARR-LOA, if varible is multidim array,
		  // after each LDC of index of each dimension.
		  // This is for var assignment case, not init.
		  if((multiarraydim>1) && (isinit==0)) {
			gen_code(bass_file, AALOAD, 0);
			multiarraydim--;
		  }
		}
		| var
		{
		  found = find_sym($1);
		  int arg = found->local_index;
		  if (found->type ==0) { // var is variable
		    printf("Found variable [%s] in [arithExpr] rule. local index is: [%s->%d]\n", found->id, found->id, arg);
		    gen_code(bass_file, ILOAD, arg);
		    // var_value is a global holder, when a var terminal token comes,
		    // then it's value set to var_value.
	   	    // Because, we are doing linear translation, so in each time,
		    // we have translate only one instruction.
		    var_value = found->value;
		  }
		  else if (found->type ==1) { // var is array
		    gen_code(bass_file, IALOAD, 0);
		    var_value = found->value;
		  }
		  //var_id = found->id;
		}
		| '(' arithExpr ')'
		;

boolExpr	: boolExpr OR boolExpr { operator = 7; }
	 	| boolExpr AND boolExpr { operator = 6; }
		| NOT boolExpr { operator = 8; }
	 	| arithExpr GREATERTHAN arithExpr { operator = 0; }
		| arithExpr GREATEREQ arithExpr { operator = 1; }
		| arithExpr LESSTHAN arithExpr { operator = 2; }
	 	| arithExpr LESSEQ arithExpr { operator = 3; }
		| arithExpr EQUAL arithExpr { operator = 4; }
		| arithExpr NOTEQ arithExpr { operator = 5; }
		| arithExpr
		;

	
%%	/* ----- USER SUBROUTINES PART ----- */
	/* ================================= */


#include "lex.yy.c"

yyerror(const char *msg)
{
	printf("Fail (around line %d)\n", yylineno);
}

main()
{
	int ret = -1;
	bass_file = fopen("Program.bass", "w");
	
	ret = yyparse();
	if (ret == 0) {
		puts("Pass");
		puts("Created a BASS file: [Program.bass]");
		puts("You can use bass.jar to assemble it into java's [Program.class] file;)");
	}
		
	fclose(bass_file);
	return 0;
}
